package com.globalwave.common.db;


import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.List;
import java.util.Map;




import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.LogFactoryImpl;
import org.hibernate.SessionFactory;

import com.globalwave.common.exception.BusinessException;


public class BaseJDBCDAO {
	
    protected Log log = LogFactoryImpl.getLog(this.getClass()) ;
    
    protected Connection connection ;
    protected boolean isFileObject = false ;
    
    private StatementManager statementManager ;
    
    private static Map<Class<?>, Integer> typesMap = new HashMap<Class<?>, Integer>() ;
    static {
    	typesMap.put(Short.class, Types.SMALLINT) ;
    	typesMap.put(Integer.class, Types.INTEGER) ;
    	typesMap.put(Long.class, Types.BIGINT) ;
    }
    
    private SessionFactory sessionFactory ;
    
    public BaseJDBCDAO () {
    	statementManager = StatementManager.getInstance() ;
    }
    
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory ;   
    }
    
    public Connection getConnection() {
    	//if (connection == null) {
    	if (sessionFactory != null) {
    		connection = sessionFactory.getCurrentSession().connection() ;
    	}
    	//}
        return connection; 

    }
    
    public void setConnection(Connection connection) {
    	this.connection = connection ; 
    }
    
    public ResultSet query(final String sql, final Object[] params) {
    	
    	log(sql, params, log.isDebugEnabled()) ;
    	
    	PreparedStatement statement = null ;
    	int i = 0 ;
    	
    	try {
	        statement = statementManager.get(this.getConnection(), sql) ;
	
	        if (params != null) {
		        for(; i < params.length ; i ++) {
		            statement.setObject(i + 1, params[i]) ;// 索引从1开始
		        }
	        }
	        
	        ResultSet rs = statement.executeQuery() ;
	        
	        return rs ;
    	} catch (Exception e) {
    		
		    log(sql.toString(), params, i) ;
		    
			log.error(e, e) ;
			
    		throw new BusinessException(e, "1000") ;
    	}
    }


    public Long getLong(final String sql, final Object[] params) {
    	return getLong(sql, params, null) ;
    }
    
    public Long getLong(final String sql, final Object[] params, String colName) {
    	ResultSet rs = null ;
    	
    	try {
    		rs = query(sql, params) ;
			
    		if (rs.next()) {
    			if (colName == null) {
				    return rs.getLong(1) ;
    			} else {
    				return rs.getLong(colName) ;
    			}
			}
        	return null ;
		} catch (SQLException e) {
			log.error(e, e) ;

    		throw new BusinessException(e, "1000") ;
		} finally {
			if (rs != null) {
				try {
					rs.close() ;
				} catch (SQLException e) {
					log.error(e, e) ;
					
					throw new BusinessException(e, "1000") ;
				}
			}
		}
    }
    public String getString(final String sql, final Object[] params) {
    	return getString(sql, params, null) ;
    }
    
    public String getString(final String sql, final Object[] params, String colName) {
    	ResultSet rs = null ;
    	
    	try {
    		rs = query(sql, params) ;
			
    		if (rs.next()) {
    			if (colName == null) {
				    return rs.getString(1) ;
    			} else {
    				return rs.getString(colName) ;
    			}
			}
        	return null ;
		} catch (SQLException e) {
			log.error(e, e) ;

    		throw new BusinessException(e, "1000") ;
		} finally {
			if (rs != null) {
				try {
					rs.close() ;
				} catch (SQLException e) {
					log.error(e, e) ;
					
					throw new BusinessException(e, "1000") ;
				}
			}
		}
    }

    public int[] execute(final String sql, final List<Object[]> records) {
    	Object[] params = null ;

    	PreparedStatement statement = null ;
    	int i = 0, j = 0 ;
    	
    	try {
	        statement = statementManager.get(this.getConnection(), sql) ;
	        
	        for (; i < records.size() ; i ++) {
	        	
	        	params = records.get(i) ;

	        	log(sql, params, log.isDebugEnabled()) ;
	        	
		        for(; j < params.length ; j ++) {
		            final Object fieldObj = params[j] ;
		            if (isFileObject && fieldObj instanceof File){
		                final File file = (File)fieldObj ;
		                int len = (int) file.length();
		                InputStream fis = new FileInputStream(file);
		                statement.setBinaryStream(j + 1,fis,len);
		            }  else {	            	
		                statement.setObject(j + 1, fieldObj) ;//
		            }
		        }
		        
		        statement.addBatch() ;
	        }
	        
	        return statement.executeBatch() ;
	        
    	} catch (Exception e) {
    		
		    log(sql.toString(), params, j * 10000 + i) ;
		    
			log.error(e, e) ;
			
    		throw new BusinessException(e, "1000") ;
    	}
    	
    }
    
    public int execute(final String sql, final Object[] params) {
    	PreparedStatement statement = null ;
    	int i = 0, result = 0 ;
    	
    	try {
	        statement = statementManager.get(this.getConnection(), sql) ;

	    	log(sql, params, log.isDebugEnabled()) ;
	    	
	        for(; i < params.length ; i ++) {
	            final Object fieldObj = params[i] ;
	            if (isFileObject && fieldObj instanceof File){
	                final File file = (File)fieldObj ;
	                int len = (int) file.length();
	                InputStream fis = new FileInputStream(file);
	                statement.setBinaryStream(i + 1,fis,len);
	            }  else {	            	
	                statement.setObject(i + 1, fieldObj) ;//
	            }
	        }
	        
	        result = statement.executeUpdate() ;
	        
    	} catch (Exception e) {
    		
		    log(sql.toString(), params, i) ;
		    
		    if ((i + 1)!= params.length) {
		    	log.error("Exception is throwed when setting object, index is:" + i) ;
		    }
			log.error(e, e) ;
			
    		throw new BusinessException(e, "1000") ;
    	}
    	
        return result;
    }
    

    public void log(String sql, Object[] params) {
    	log(sql, params, -1) ;
    }

    public void log(String sql, Object[] params, int index) {
    	log(sql, params, index, true) ;
    }

    public void log(String sql, Object[] params, boolean isEnabled) {
    	log(sql, params, -1, true) ;
    }

    public void log(String sql, Object[] params, int index,  boolean isEnabled) {
    	if (!isEnabled) {
    		return ;
    	}
    	
	    if (params != null && index != -1 && (index + 1)!= params.length) {
	    	log.error("Exception is throwed when setting object, index is:" + index) ;
	    }
    	    	
    	StringBuffer bf = new StringBuffer() ;
    	
    	bf.append("[") ;
    	
    	if (params != null) {
	    	for (int i = 0 ; i < params.length ; i ++) {
	    		if (i != 0) {
	    			bf.append(" ,") ;
	    		}
	    		
	    		Object value = params[i] ;
	    		
	    		if (value instanceof String) {
	    			value = "'" + value + "'" ;
	    		}
	    		
	    		bf.append(value) ;
	    	}
    	}

    	bf.append("]") ;
    	
    	StringBuffer logOut = new StringBuffer() ;
    	logOut.append("\n") ;
    	logOut.append(sql) ;
    	logOut.append("\n\n") ;
    	logOut.append(bf) ;
    	logOut.append("\n") ;

    	if (log.isDebugEnabled()) {
    	    log.debug(logOut.toString()) ;
    	} else {
    	    log.error(logOut.toString()) ;
    	}
    }

}
